﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Office.Server.Search.Query;
using Trentacular.SharePoint.Util;
using Trentacular.Office.Server.Web.UI.WebControls;

namespace Trentacular.Office.Server.Search.FacetedSearch.Providers
{
    /// <summary>
    /// A facet provider implementation that aggregates results based on the
    /// IsDocument and FileExtension managed properties into document types.
    /// </summary>
    public class DocumentTypeFacetProvider : BaseFacetProvider
    {
        private IList<DocumentType> _documentTypes;

        /// <summary>
        /// Gets the configured document types.
        /// </summary>
        /// <value>The configured document types.</value>
        protected virtual IList<DocumentType> DocumentTypes
        {
            get
            {
                if (_documentTypes == null)
                    _documentTypes = LoadDocumentTypesFromConfiguration();
                return _documentTypes;
            }
        }

        /// <summary>
        /// Loads the document types from configuration properties.
        /// </summary>
        /// <returns>List of Document Types.</returns>
        protected IList<DocumentType> LoadDocumentTypesFromConfiguration()
        {
            var documentTypes = new List<DocumentType>();

            var properties = Configuration.Properties
                .ToDictionary(p => p.Key, p => p.Value);

            foreach (var key in properties.Keys)
            {
                if (key.EndsWith("DisplayName"))
                {
                    var keyPrefix = key.Remove(key.Length - 11);
                    var keyFormat = string.Format("{0}{{0}}", keyPrefix);
                    var fileExtensionsKey = string.Format(keyFormat, "FileExtensions");
                    var iconPathKey = string.Format(keyFormat, "IconPath");

                    var documentType = new DocumentType
                    {
                        DisplayName = properties[key]
                    };

                    if (properties.ContainsKey(fileExtensionsKey))
                        documentType.FileExtensions =
                            properties[fileExtensionsKey].Split(',', ';');

                    if (properties.ContainsKey(iconPathKey))
                        documentType.IconPath = properties[iconPathKey];

                    documentTypes.Add(documentType);
                }
            }

            return documentTypes;
        }

        /// <summary>
        /// Gets the managed properties that need to be included in the facet query.
        /// </summary>
        /// <value>The managed properties to be included in the facet query.</value>
        public override string[] ManagedPropertiesToQuery
        {
            get { return new[] { "IsDocument", "FileExtension" }; }
        }

        private int isDocumentFieldIndex;
        private int fileExtensionFieldIndex;

        /// <summary>
        /// Initializes the facet provider before any rows have been iterated.
        /// </summary>
        /// <param name="resultTable">The result table.</param>
        public override void Initialize(ResultTable resultTable)
        {
            isDocumentFieldIndex = resultTable.GetOrdinal("IsDocument");
            fileExtensionFieldIndex = resultTable.GetOrdinal("FileExtension");
        }

        /// <summary>
        /// Processes a single row of the result table.
        /// </summary>
        /// <param name="resultTable">The result table.</param>
        public override void ProcessRow(ResultTable resultTable)
        {
            // If DBNull, lets get out of here
            if (resultTable.IsDBNull(isDocumentFieldIndex) ||
                resultTable.IsDBNull(fileExtensionFieldIndex))
                return;

            // If not a Document, get out of here
            if (resultTable.GetInt16(isDocumentFieldIndex) <= 0)
                return;

            var fileExtension = resultTable.GetString(fileExtensionFieldIndex);

            foreach (var documentType in this.DocumentTypes)
            {
                // When there are no file extensions, treat as "All Documents" type
                if (documentType.FileExtensions == null ||
                    documentType.FileExtensions.Length == 0 ||
                    documentType.FileExtensions.Contains(
                        fileExtension,
                        StringComparer.InvariantCultureIgnoreCase
                        )
                    )
                {
                    ProcessFacetHit(documentType);
                }
            }
        }

        private void ProcessFacetHit(DocumentType documentType)
        {
            var key = documentType.DisplayName;
            if (this.FacetTable.ContainsKey(key))
            {
                this.FacetTable[key].Count++;
                return;
            }

            var filterString = (documentType.FileExtensions == null) ?
                "(IsDocument = 1)" :
                string.Format("(IsDocument = 1 And ({0}))",
                    documentType.FileExtensions
                        .Select(fe => string.Format("FileExtension = '{0}'", fe))
                        .Delimit(" Or ")
                    );

            var facet = new Facet(
                FacetedSearchContext,
                Configuration,
                key,
                key,
                filterString
            );

            facet.Count = 1;
            facet.CustomRenderer = new IconFacetRenderer
            {
                Facet = facet,
                ImageSrc = documentType.IconPath,
                ApplyAlphaFilter = true
            };

            this.FacetTable[key] = facet;
        }
    }
}
